package org.beanutopia.function;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.SortedArraySet;

import java.util.Comparator;
import java.util.Iterator;

/**
 * @author ymeymann
 * @since Dec 5, 2007 2:12:48 PM
 */
public class Policy<F,T> implements Iterable<Policy.Rule<F,T>>, Function<F,T> {
  private SortedArraySet<Rule<F,T>> set = null;
  private static final Comparator<Rule<?,?>> COMPARATOR = new Comparator<Rule<?,?>>() {
    public int compare(Rule<?, ?> o1, Rule<?, ?> o2) {
      return PredicateComparator.getInstance().compare(o1.predicate(), o2.predicate());
    }
  };

  public Policy() {
    this(1);
  }

  public Policy(int capacity) {
    set = new SortedArraySet<Rule<F,T>>(COMPARATOR, capacity);
  }


  public T apply(F from) {
    Rule<F,T> rule = getRule(from);
    return rule == null ? null : rule.decision();
  }

  public Rule<F,T> getRule(F from) {
    for (Rule<F,T> rule: this) {
      if (rule.predicate().apply(from)) return rule;
    }
    return null;
  }

//  public <C extends F> Policy<F,T> addRule(Class<C> c, T action) {
//    add(new Rule<F,T>(isInstanceOf(c), action));
//    return this;
//  }

  public Policy<F,T> addRule(Predicate<? super F> condition, T action) {
    set.add(new Rule<F,T>(condition, action));
    return this;
  }

  public Policy<F,T> addRule(T defaultAction) {
    addRule(PredicateComparator.ALWAYS_TRUE, defaultAction);
    return this;
  }

  //-- set implementation --//

  public int size() {
    return set.size();
  }

  public boolean isEmpty() {
    return set.isEmpty();
  }

  public Iterator<Rule<F, T>> iterator() {
    return set.iterator();
  }

  public Object[] toArray() {
    return set.toArray();
  }

  public <T> T[] toArray(T[] a) {
    return set.toArray(a);
  }

  public void clear() {
    set.clear();
  }

  public Comparator<? super Rule<F, T>> comparator() {
    return set.comparator();
  }

  public Rule<F, T> first() {
    return set.first();
  }

  public void trimToSize() {
    set.trimToSize();
  }

  public Rule<F, T> last() {
    return set.last();
  }

  public Policy<F,T> addNullSafetyRule() {
    addRule(PredicateComparator.IS_NULL, null);
    return this;
  }

  public static final class Rule<A,B> {
    private final Predicate<? super A> predicate;
    private final B decision;

    public Rule(Predicate<? super A> predicate, B object) {
      this.predicate = predicate;
      this.decision = object;
    }

    public Predicate<? super A> predicate() {
      return predicate;
    }

    public B decision() {
      return decision;
    }
  }

}
